const { findUser } = require('./account');
const { userCollection, LOG_TYPE } = require('../../common/constants');
const { ERROR } = require('../../common/error');
const { logout } = require('./logout');
const PasswordUtils = require('./password');

async function realPreLogin(params = {}) {
    const { user } = params;
    const appId = this.getUniversalClientInfo().appId;
    const { total, userMatched } = await findUser({
        userQuery: user,
        authorizedApp: appId,
    });
    if (userMatched.length === 0) {
        if (total > 0) {
            throw {
                errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP,
            };
        }
        throw {
            errCode: ERROR.ACCOUNT_NOT_EXISTS,
        };
    } else if (userMatched.length > 1) {
        throw {
            errCode: ERROR.ACCOUNT_CONFLICT,
        };
    }
    const userRecord = userMatched[0];
    checkLoginUserRecord(userRecord);
    return userRecord;
}

async function preLogin(params = {}) {
    const { user } = params;
    try {
        const user = await realPreLogin.call(this, params);
        return user;
    } catch (error) {
        await this.middleware.uniIdLog({
            success: false,
            data: user,
            type: LOG_TYPE.LOGIN,
        });
        throw error;
    }
}

async function preLoginWithPassword(params = {}) {
    const { user, password } = params;
    try {
        const userRecord = await realPreLogin.call(this, params);
        const { passwordErrorLimit, passwordErrorRetryTime } = this.config;
        const { clientIP } = this.getUniversalClientInfo();
        // 根据ip地址，密码错误次数过多，锁定登录
        let loginIPLimit = userRecord.login_ip_limit || [];
        // 清理无用记录
        loginIPLimit = loginIPLimit.filter((item) => item.last_error_time > Date.now() - passwordErrorRetryTime * 1000);
        let currentIPLimit = loginIPLimit.find((item) => item.ip === clientIP);
        if (currentIPLimit && currentIPLimit.error_times >= passwordErrorLimit) {
            throw {
                errCode: ERROR.PASSWORD_ERROR_EXCEED_LIMIT,
            };
        }
        const passwordUtils = new PasswordUtils({
            userRecord,
            clientInfo: this.getUniversalClientInfo(),
            passwordSecret: this.config.passwordSecret,
        });

        const { success: checkPasswordSuccess, refreshPasswordInfo } = passwordUtils.checkUserPassword({
            password,
        });
        if (!checkPasswordSuccess) {
            // 更新用户ip对应的密码错误记录
            if (!currentIPLimit) {
                currentIPLimit = {
                    ip: clientIP,
                    error_times: 1,
                    last_error_time: Date.now(),
                };
                loginIPLimit.push(currentIPLimit);
            } else {
                currentIPLimit.error_times++;
                currentIPLimit.last_error_time = Date.now();
            }
            await userCollection.doc(userRecord._id).update({
                login_ip_limit: loginIPLimit,
            });
            throw {
                errCode: ERROR.PASSWORD_ERROR,
            };
        }
        const extraData = {};
        if (refreshPasswordInfo) {
            extraData.password = refreshPasswordInfo.passwordHash;
            extraData.password_secret_version = refreshPasswordInfo.version;
        }

        const currentIPLimitIndex = loginIPLimit.indexOf(currentIPLimit);
        if (currentIPLimitIndex > -1) {
            loginIPLimit.splice(currentIPLimitIndex, 1);
        }
        extraData.login_ip_limit = loginIPLimit;
        return {
            user: userRecord,
            extraData,
        };
    } catch (error) {
        await this.middleware.uniIdLog({
            success: false,
            data: user,
            type: LOG_TYPE.LOGIN,
        });
        throw error;
    }
}

function checkLoginUserRecord(user) {
    switch (user.status) {
        case undefined:
        case 0:
            break;
        case 1:
            throw {
                errCode: ERROR.ACCOUNT_BANNED,
            };
        case 2:
            throw {
                errCode: ERROR.ACCOUNT_AUDITING,
            };
        case 3:
            throw {
                errCode: ERROR.ACCOUNT_AUDIT_FAILED,
            };
        case 4:
            throw {
                errCode: ERROR.ACCOUNT_CLOSED,
            };
        default:
            break;
    }
}

async function thirdPartyLogin(params = {}) {
    const { user } = params;
    return {
        mobileComfirmd: user.mobile_comfirmd,
        emailComfirmd: user.email_comfirmd,
    };
}

async function postLogin(params = {}) {
    const { user, extraData, isThirdParty = false } = params;
    const { clientIP } = this.getUniversalClientInfo();
    const uniIdToken = this.getUniversalUniIdToken();
    const uid = user._id;
    const updateData = {
        last_login_date: Date.now(),
        last_login_ip: clientIP,
        ...extraData,
    };
    const { token, tokenExpired } = await this.uniIdCommon.createToken({
        uid,
    });

    if (uniIdToken) {
        try {
            await logout.call(this);
        } catch (error) {}
    }

    await userCollection.doc(uid).update(updateData);
    await this.middleware.uniIdLog({
        data: {
            user_id: uid,
        },
        type: LOG_TYPE.LOGIN,
    });
    return {
        errCode: 0,
        newToken: {
            token,
            tokenExpired,
        },
        uid,
        ...(isThirdParty
            ? thirdPartyLogin({
                  user,
              })
            : {}),
        passwordConfirmed: !!user.password,
    };
}

module.exports = {
    preLogin,
    postLogin,
    checkLoginUserRecord,
    preLoginWithPassword,
};
